package com.ithuameng.admin.config;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Swagger 配置
 *
 * @author ithuameng
 */
@Configuration
public class SwaggerConfig {

	private static final String MULITI_PACKAGE_SPLITER = ",";

	private static final String DEFAULT_INCLUDE_PATTERN = "^/(?!public).*$";

	@Resource
	private AdminCfgProp cfgProp;

	@Bean
	public Docket api() {
		Docket docket = new Docket(DocumentationType.OAS_30).apiInfo(apiEndPointsInfo())
				.enable(cfgProp.getSwagger().isEnable())
				.directModelSubstitute(java.time.LocalDate.class, java.sql.Date.class)
				.directModelSubstitute(java.time.ZonedDateTime.class, Date.class)
				.directModelSubstitute(java.time.LocalDateTime.class, Date.class);
		if (cfgProp.getSwagger().getSecurity() != null && cfgProp.getSwagger().getSecurity()) {
			docket.securityContexts(Lists.newArrayList(securityContext())).securitySchemes(securitySchemes());
		}

		docket = docket.select().apis(SwaggerConfig.basePackage(cfgProp.getSwagger().getBasePackage()))
				.paths(PathSelectors.any()).build();
		return docket;
	}

	private ApiInfo apiEndPointsInfo() {
		return new ApiInfoBuilder().title(cfgProp.getSwagger().getTitle())
				.description(cfgProp.getSwagger().getDescription())
				.contact(new Contact(cfgProp.getSwagger().getContact().getName(),
						cfgProp.getSwagger().getContact().getUrl(), cfgProp.getSwagger().getContact().getEmail()))
				.license("Apache 2.0").licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
				.version(cfgProp.getSwagger().getVersion()).build();
	}

	/**
	 * 设置授权信息
	 */
	private List<SecurityScheme> securitySchemes() {
		List<SecurityScheme> securitySchemes = new ArrayList<>();
		securitySchemes.add(HttpAuthenticationScheme.BASIC_AUTH_BUILDER.name("basic").build());
		securitySchemes.add(HttpAuthenticationScheme.JWT_BEARER_BUILDER.name("jwt").build());
		return securitySchemes;
	}

	@SuppressWarnings("deprecation")
	private SecurityContext securityContext() {

		return SecurityContext.builder().securityReferences(securityReferences())
				.forPaths(PathSelectors.regex(DEFAULT_INCLUDE_PATTERN)).build();
	}

	public static Predicate<RequestHandler> basePackage(final String basePackage) {
		return input -> declaringClass(input).transform(handlerPackage(basePackage)).or(true);
	}

	private static Function<Class<?>, Boolean> handlerPackage(final String basePackage) {
		return input -> {
			// 循环判断匹配
			for (String strPackage : basePackage.split(MULITI_PACKAGE_SPLITER)) {
				boolean isMatch = input.getPackage().getName().startsWith(strPackage);
				if (isMatch) {
					return true;
				}
			}
			return false;
		};
	}

	@SuppressWarnings("deprecation")
	private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
		return Optional.fromNullable(input.declaringClass());
	}

	private List<SecurityReference> securityReferences() {
		AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
		AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
		authorizationScopes[0] = authorizationScope;

		List<SecurityReference> securityReferences = new ArrayList<>();
		securityReferences.add(new SecurityReference("jwt", authorizationScopes));
		securityReferences.add(new SecurityReference("basic", authorizationScopes));
		return securityReferences;
	}
}
